﻿using Newtonsoft.Json.Linq;
using SageKing.Extensions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace SageKing.Core.Extensions;

/// <summary>
/// 字典类型拓展类
/// </summary>
[SuppressSniffer]
public static class DictionaryExtensions
{
    /// <summary>
    /// 将对象转字典类型，其中值返回原始类型 Type 类型
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public static IDictionary<string, Tuple<Type, object>> ToDictionaryWithType(this object input)
    {
        if (input == null) return default;

        // 处理本就是字典类型
        if (input.GetType().HasImplementedRawGeneric(typeof(IDictionary<,>)))
        {
            var dicInput = ((IDictionary)input);

            var dic = new Dictionary<string, Tuple<Type, object>>();
            foreach (var key in dicInput.Keys)
            {
                var value = dicInput[key];
                var tupleValue = value == null ?
                    new Tuple<Type, object>(typeof(object), value) :
                    new Tuple<Type, object>(value.GetType(), value);

                dic.Add(key.ToString(), tupleValue);
            }

            return dic;
        }

        var dict = new Dictionary<string, Tuple<Type, object>>();

        // 获取所有属性列表
        foreach (var property in input.GetType().GetProperties())
        {
            dict.Add(property.Name, new Tuple<Type, object>(property.PropertyType, property.GetValue(input, null)));
        }

        // 获取所有成员列表
        foreach (var field in input.GetType().GetFields())
        {
            dict.Add(field.Name, new Tuple<Type, object>(field.FieldType, field.GetValue(input)));
        }

        return dict;
    }

    /// <summary>
    /// 获取成员值
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="member"></param>
    /// <returns></returns>
    private static object GetValue(object obj, MemberInfo member)
    {
        if (member is PropertyInfo info)
            return info.GetValue(obj, null);

        if (member is FieldInfo info1)
            return info1.GetValue(obj);

        throw new ArgumentException("Passed member is neither a PropertyInfo nor a FieldInfo.");
    }

    public static bool TryGetValue<T>(this IDictionary<string, object> dictionary, string key, out T value) => dictionary.TryGetValue<string, T>(key, out value);
    public static bool TryGetValue<T>(this IDictionary<string, object> dictionary, IEnumerable<string> keys, out T value) => dictionary.TryGetValue<string, T>(keys, out value);
    public static bool TryGetValue<T>(this IDictionary<object, object> dictionary, string key, out T value) => dictionary.TryGetValue<object, T>(key, out value);

    public static bool TryGetValue<TKey, T>(this IDictionary<TKey, object> dictionary, TKey key, out T value)
    {
        if (!dictionary.TryGetValue(key, out var item))
        {
            value = default!;
            return false;
        }

        value = ConvertValue<T>(item);
        return true;
    }

    public static bool TryGetValue<TKey, T>(this IDictionary<TKey, object> dictionary, IEnumerable<TKey> keys, out T value)
    {
        foreach (var key in keys)
        {
            if (dictionary.TryGetValue(key, out var item))
            {
                value = ConvertValue<T>(item);
                return true;
            }
        }

        value = default!;
        return false;
    }

    public static T? GetValue<TKey, T>(this IDictionary<TKey, T> dictionary, TKey key) => ConvertValue<T>(dictionary[key]);
    public static T? GetValue<T>(this IDictionary<string, object> dictionary, string key) => ConvertValue<T>(dictionary[key]);

    public static T? GetValueOrDefault<TKey, T>(this IDictionary<TKey, object> dictionary, TKey key, Func<T?> defaultValueFactory) => TryGetValue<TKey, T>(dictionary, key, out var value) ? value : defaultValueFactory();
    public static T? GetValueOrDefault<TKey, T>(this IDictionary<TKey, object> dictionary, TKey key) => GetValueOrDefault<TKey, T>(dictionary, key, () => default);
    public static T? GetValueOrDefault<T>(this IDictionary<string, object> dictionary, string key, Func<T?> defaultValueFactory) => TryGetValue<T>(dictionary, key, out var value) ? value : defaultValueFactory();
    public static T? GetValueOrDefault<T>(this IDictionary<string, object> dictionary, IEnumerable<string> keys, Func<T?> defaultValueFactory) => TryGetValue<T>(dictionary, keys, out var value) ? value : defaultValueFactory();
    public static T? GetValueOrDefault<T>(this IDictionary<string, object> dictionary, string key) => GetValueOrDefault<T>(dictionary, key, () => default);
    public static object? GetValueOrDefault(this IDictionary<string, object> dictionary, string key) => GetValueOrDefault<object>(dictionary, key, () => default);

    public static T GetOrAdd<TKey, T>(this IDictionary<TKey, T> dictionary, TKey key, Func<T> valueFactory)
    {
        if (dictionary.TryGetValue(key, out T? value))
            return value;

        value = valueFactory()!;
        dictionary.Add(key, value);
        return value;
    }

    public static T GetOrAdd<TKey, T>(this IDictionary<TKey, object> dictionary, TKey key, Func<T> valueFactory)
    {
        if (dictionary.TryGetValue<TKey, T>(key, out var value))
            return value!;

        value = valueFactory()!;
        dictionary.Add(key, value);
        return value;
    }

    public static IDictionary<string, object> AddInput<T>(this IDictionary<string, object> dictionary, T value) where T : notnull => dictionary.AddInput(typeof(T).Name, value);

    public static IDictionary<string, object> AddInput(this IDictionary<string, object> dictionary, string key, object value)
    {
        dictionary.Add(key, value);
        return dictionary;
    }

    /// <summary>
    /// Merges the specified dictionary with the other dictionary.
    /// When a key exists in both dictionaries, the value in the other dictionary will overwrite the value in the specified dictionary.
    /// </summary>
    public static void Merge(this IDictionary<string, object> dictionary, IDictionary<string, object> other)
    {
        foreach (var (key, value) in other)
            dictionary[key] = value;
    }

    private static T? ConvertValue<T>(object? value) => value.ConvertTo<T>();

    /// <summary>
    /// 合并 Key+Value->Value
    /// </summary>
    /// <typeparam name="TK"></typeparam>
    /// <typeparam name="TV"></typeparam>
    /// <param name="dic"></param>
    /// <param name="sp"></param>
    public static Dictionary<TK, TV> MergeKVCopy<TK, TV>(this Dictionary<TK, TV> dic, string sp = ":")
    {
        var result = new Dictionary<TK, TV>();
        foreach (var item in dic)
        {
            result[item.Key] = $"{item.Key}{sp}{item.Value}".ConvertTo<TV>();
        }
        return result;

    }
}